/**
 * amap-utils
 * 高德地图工具库
 * @author maybe
 */
import * as AMapLoader from '@amap/amap-jsapi-loader';
import { isTruePolygon } from './geometric'
import { mergeConfig, wrapPromise } from './utils';

import "./type"; // 类型定义

export enum VectorTypes {
    polygon = "polygon", polyline = "polyline", circle = "circle",
    bezierCurve = "bezierCurve", ellipse = 'ellipse', rectangle = 'rectangle'
};

interface Editor {
    open: () => void,
    close: () => void
    on: (name: string, func: (e) => void) => void
}

/**
 * AmapUtils
 * @remark 所有静态方法AmapUtils.func(),不依赖map和Amap的均为静态方法
 */
class AmapUtils {

    map: AMap.Map;
    AMap: typeof AMap;
    AMapUI: any;
    mobile: boolean;
    loadOpts: any;
    id: string;
    mapOpts: any;

    /**
     * @param Opts 插件配置
     */
    constructor(Opts: any) {
        if (!Opts) {
            this.error('请传入初始化配置!')
        }
        this.loadOpts = Opts;
    }

    error(msg: string) {
        console.error(`[AmapUtils Error]:${msg}`)
    }

    /**
     * 异步加载插件
     * @param plugins 
     * @returns 
     */
    loadPlugins(plugins: any[]) {
        return new Promise((res, rej) => {
            this.AMap.plugin(plugins, function (result) {
                res(result)
            });
        })
    }
    /**
  * 异步加载UI插件
  * @param plugins 
  * @returns 
  */
    loadUIPlugins(plugins: any[]) {
        return new Promise((res, rej) => {
            this.AMapUI.loadUI(plugins, function (result) {
                res(result)
            });
        })
    }

    /**
     * 兼容运行
     * @param pcFunc 
     * @param mobileFunc 
     */
    run(pcFunc, mobileFunc) {
        return this.mobile ? mobileFunc() : pcFunc();
    }

    /**
     * 初始化地图
     * @param id 容器id
     * @param Opts 配置
     * @returns 
     */
    initMap(id: string, Opts?: object): Promise<AMap.Map> {
        this.id = id;
        this.mapOpts = Opts;
        return new Promise((reslove, reject) => {
            AMapLoader.load(this.loadOpts).then((AMap) => {
                window.AMap = AMap;
                this.AMapUI = window.AMapUI;
                this.AMap = AMap;
                this.map = new AMap.Map(id, Opts);

                // 检测运行平台是否为移动端设备
                this.mobile = this.AMap.Browser.mobile;
                console.log(`当前运行终端设备为${this.mobile ? '移动端' : 'PC端'}`)
                this.map.setFitView([])
                reslove(this.map)
            }).catch(e => {
                console.log(e);
                reject(e)
            })
        })
    }

    /**
     * 添加组件到地图
     * @param com 组件对象组
     * @param autoFit 自动缩放地图到合适的视野级别 默认 true
     */
    addToMap(coms: any[], autoFit = true) {
        // 将多边形添加到地图
        this.map.add(coms)
        // 缩放地图到合适的视野级别
        autoFit && this.map.setFitView(coms)
    }

    /**
     * 鼠标工具
     * @returns 
     */
    mouseTool() {
        return new (this.AMap as any).MouseTool(this.map);
    }

    /**
     * 删除覆盖物/图层。参数为单个覆盖物/图层，或覆盖物/图层的数组
     * @desc 参数说明：
    features ((Overlay | Layer | Array<(Overlay | Layer)>)) 覆盖物对象或者数组
     */
    remove(features) {
        return this.map.remove(features)
    }

    /**
     * 创建矢量图编辑器
     * @param type 类型 例: polygon
     * @param com 组件对象 例: Polygon对象 同高德Editor方法
     * @param opts 额外配置 同高德Editor方法
     * @returns 
     */
    createVectorEditor(type: VectorTypes, com: any, opts?: any) {
        let editor: Editor;

        let utype = '';

        switch (type) {
            case VectorTypes.circle:
                utype = 'CircleEditor';
                break;
            case VectorTypes.polygon:
                utype = 'PolygonEditor';
                break;
            case VectorTypes.bezierCurve:
                utype = 'BezierCurveEditor';
                break;
            case VectorTypes.polyline:
                utype = 'PolylineEditor';
                break;
            case VectorTypes.ellipse:
                utype = 'EllipseEditor';
                break;
            case VectorTypes.rectangle:
                utype = 'RectangleEditor';
                break;
            default:
                break;
        }

        editor = new (this.AMap[utype] as any)(this.map, com, opts)
        return editor;
    }

    /**
     * 创建矢量图形
     * @param type 类型
     * @param opts 配置
     * @param intomap 渲染在地图中 默认true
     * @returns 
     */
    createVector(type: VectorTypes, opts?: any, intomap = true) {
        let vec;
        let baseOpts = {
            fillColor: '#00b0ff',
            strokeColor: '#80d8ff'
        }
        // 合并配置
        opts = mergeConfig(baseOpts, opts)

        type = type[0].toUpperCase() + type.substring(1) as any;

        vec = new this.AMap[`${type}`](opts)
        if (intomap) {
            this.addToMap(vec, true)
        }
        return vec;
    }

    /**
     * 判断坐标组成的单个多边形是否合法
     * @param {*} com 
     * @description 请传入组件对象obj或者[[1,2],[2,2],[3,3]] 类似的二维数组 
     * @returns {boolean}
     */
    static isTruePolygon(target: [][] | any) {
        if (!target) return false;
        if (!Array.isArray(target)) {
            target = this.getPath(target)
        }
        return isTruePolygon(target);
    }


    /**
     * 坐标结构转换[{lng:'',lat:''}] => [[lng,lat]]
     */
    static objArrToArr(paths) {
        return paths.map(item => [item.lng, item.lat]);
    }

    /**
     * 根据组件object获取二维数组[[111,99]] 
     * @param obj 
     * @returns 
     */
    static getPath(obj) {
        return this.objArrToArr(obj.getPath())
    }

    /**
     * 获取编辑器组件对象
     * @param editor 
     * @returns 
     */
    getTarget(editor) {
        if (!editor) {
            console.error('请传入编辑器')
            return false
        };
        return editor.getTarget();
    }

    /**
     * 打开编辑器
     * @param editor 
     * @returns 
     */
    openEditor(editor) {
        if (!editor) {
            console.error('请传入编辑器')
            return false
        };
        return editor.open()
    }

    /**
     * 关闭编辑器
     * @param editor 
     * @returns 
     */
    closeEditor(editor) {
        if (!editor) {
            console.error('请传入编辑器')
            return false
        };
        editor.close()
        return;
    }

    /**
     * 设置地图中心
     * @param lnglat [xxx,xx]
     * @param zoom 地图层级
     */
    setCenter(lnglat: [number, number], zoom?: number) {
        this.map.setZoomAndCenter(zoom, lnglat); //同时设置地图层级与中心点
    }

    /**
     * 获取定位（浏览器定位）
     * @param conf Geolocation 配置 默认配置
     * ```
     *  {
     *          // 是否使用高精度定位，默认：true
                enableHighAccuracy: true,
                // 设置定位超时时间，默认：无穷大
                timeout: 10000,
                // 定位按钮的停靠位置的偏移量
                offset: [10, 20],
                //  定位成功后调整地图视野范围使定位位置及精度范围视野内可见，默认：false
                zoomToAccuracy: true,
                //  定位按钮的排放位置,  RB表示右下
                position: 'RB'}
     * ```
     * @param onComplete 定位成功回调
     * @param onComplete 定位失败回调
     * @returns 
     */
    async getLocation(conf?: {
        conf?: any,
        onComplete?: (res: any) => void,
        onError?: (res: any) => void,
    }) {

        return await wrapPromise(async (reslove, reject) => {

            const self = this;

            await this.loadPlugins(['AMap.Geolocation'])

            const Opts = mergeConfig({
                // 是否使用高精度定位，默认：true
                enableHighAccuracy: true,
                // 设置定位超时时间，默认：无穷大
                timeout: 10000,
                // 定位按钮的停靠位置的偏移量
                offset: [10, 20],
                //  定位成功后调整地图视野范围使定位位置及精度范围视野内可见，默认：false
                zoomToAccuracy: true,
                //  定位按钮的排放位置,  RB表示右下
                position: 'RB'
            }, conf.conf);

            const geolocation = new this.AMap.Geolocation(Opts)

            geolocation.getCurrentPosition(function (status, result) {
                if (status == 'complete') {
                    onComplete(result)
                } else {
                    onError(result)
                }
            });

            function onComplete(data) {
                // data是具体的定位信息
                if (conf.onComplete) {
                    conf.onComplete(data)
                }


                reslove(data)

            }

            function onError(data) {
                // 定位出错
                if (conf.onError) {
                    conf.onError(data)
                }
                reject(data)
            }
        })
    }

    /**
  * 添加标记点
  * @param {*} lnglat  [经度,纬度]
  */
    addMarker(lnglat: [number, number], opts?: AMap.MarkerOptions) {
        var marker = new this.AMap.Marker(mergeConfig({
            icon: "//webapi.amap.com/theme/v1.3/markers/n/mark_b.png",
            position: lnglat,
        }, opts));
        marker.setMap(this.map);
        return marker;
    }

    /**
     * 绘制矢量图形
     * @param type 
     * @param conf 
     * @returns 
     */
    async drawVector(type: VectorTypes, conf?: {
        circleOpts?: AMap.CircleMarkerOptions,
        polygonOpts?: AMap.PolygonOptions,
        positionPickerOpts?: object,
        markerOpts?: AMap.MarkerOptions,
        /**
         * 绘制完成回调
         */
        onFinish?: (result: { editor: Editor, type: string }) => void,
        /**
         * 移动端拖拽选址
         */
        onPickerSuccess?: (result: any) => void
    }) {
        const self = this;

        if (this.mobile) {

            const baseOpts = {
                mode: 'dragMap',//设定为拖拽地图模式，可选'dragMap'、'dragMarker'，默认为'dragMap'
                map: this.map//依赖地图对象
            }
            let path = [];
            let currentPr;
            let editor: Editor;
            let obj;
            let markers = []


            const PositionPicker: any = await this.loadUIPlugins(['misc/PositionPicker'])

            const positionPicker = new PositionPicker(mergeConfig(baseOpts, conf.positionPickerOpts));
            //TODO:事件绑定、结果处理等
            positionPicker.on('success', function (positionResult) {
                console.log(positionResult)
                currentPr = positionResult;
                conf.onPickerSuccess && conf.onPickerSuccess(positionResult)
            });
            positionPicker.on('fail', function (positionResult) {
                console.log(positionResult)
            });

            positionPicker.start();

            const clearMarkers = () => {
                // 清除预制marker
                markers.length > 0 && self.map.remove(markers)
            }


            const reset = () => {
                path.length = 0
                currentPr = null
                positionPicker.stop();
            }
            return {
                addPoint() {
                    if (!currentPr) return;
                    switch (type) {
                        case VectorTypes.polygon:
                            if (path.length < 3) {
                                const cPath: [number, number] = [currentPr.position.lng, currentPr.position.lat];
                                path.push(cPath)
                                const marker = self.addMarker(cPath, conf.markerOpts)
                                markers.push(marker)
                            }

                            if (path.length == 3) {
                                clearMarkers();
                                const obj = new self.AMap.Polygon(mergeConfig({
                                    path
                                }, conf.polygonOpts))
                                self.addToMap([obj])
                                editor = self.createVectorEditor(VectorTypes.polygon, obj);
                                conf.onFinish({ editor, type })
                                reset();
                            }
                            break;
                        case VectorTypes.circle:
                            const cPath = [currentPr.position.lng, currentPr.position.lat];
                            path.push(cPath)
                            const obj = new self.AMap.Circle(mergeConfig({
                                center: path[0],
                                radius: 1000, //半径
                            }, conf.circleOpts))

                            editor = self.createVectorEditor(VectorTypes.circle, obj);
                            self.addToMap([obj])
                            conf.onFinish({ editor, type })
                            reset();
                            break;
                        default:
                            break;
                    }
                }
            }

        } else {
            let editor: Editor;
            const mouseTool = this.mouseTool();
            // 画具体图形
            mouseTool[type.toLowerCase()](conf[`${type}Opts`]);
            // 监听绘制完成事件
            mouseTool.on('draw', (e) => {
                console.log(e)
                const obj = e.obj;
                if (type == VectorTypes.polygon) {
                    // let check = AmapUtils.isTruePolygon(obj);
                    // console.log('验证多边形合法结果：', check)

                    editor = self.createVectorEditor(VectorTypes.polygon, obj);
                    editor.on('end', (e) => {
                        console.log('多边形编辑器结果', e)
                    })
                }
                if (type == VectorTypes.circle) {
                    editor = self.createVectorEditor(VectorTypes.circle, obj);
                }

                mouseTool.close();

                conf.onFinish && conf.onFinish({ editor, type })
            })

        }

    }

    /**
     * 轨迹
     * @param {{
        markerOpts: object,
        lineArr: [number, number][]
        mapOpts?: object,
        polylineOpts?: object,
        passedPolylineOpts?: object,
        moveAlongOpts?: object,
        onMarkerMoving: (e: any) => void
    }} Opts 配置
     * @returns trackUtils 轨迹动画控制
     */
    async track(Opts?: {
        /**
         * 覆盖物点（必填）
         */
        markerOpts: AMap.MarkerOptions,
        /**
         * 路径数组（必填）
         */
        lineArr: [number, number][]
        mapOpts?: object,
        polylineOpts?: AMap.PolygonOptions,
        passedPolylineOpts?: object,
        /**
         * 以指定的时长，点标记沿指定的路径移动的配置
         * @demo https://lbs.amap.com/demo/jsapi-v2/example/marker/replaying-historical-running-data
         */
        moveAlongOpts?: object,
        onMarkerMoving: (e: any) => void
    }) {

        await this.loadPlugins(['AMap.MoveAnimation'])

        Opts = mergeConfig({
            lineArr: [],
            mapOpts: {},
            markerOpts: {},
            polylineOpts: {},
            passedPolylineOpts: {},
            moveAlongOpts: {}
        }, Opts)



        // debugger;
        let self = this;
        let marker: AMap.Marker;


        marker = new self.AMap.Marker(mergeConfig({
            map: this.map,
            position: [],
            icon: "https://a.amap.com/jsapi_demos/static/demo-center-v2/car.png",
            offset: new AMap.Pixel(-13, -26),
        }, Opts.markerOpts));

        // 绘制轨迹
        var polyline = new self.AMap.Polyline(mergeConfig({
            path: Opts.lineArr,
            showDir: true,
            strokeColor: "#28F",  //线颜色
            // strokeOpacity: 1,     //线透明度
            strokeWeight: 6,      //线宽
            // strokeStyle: "solid"  //线样式
        }, Opts.polylineOpts));

        var passedPolyline = new self.AMap.Polyline(mergeConfig({
            strokeColor: "#AF5",  //线颜色
            strokeWeight: 6,      //线宽
        }, Opts.passedPolylineOpts));

        this.map.add([polyline, passedPolyline])

        marker.on('moving', function (e) {
            passedPolyline.setPath(e.passedPath);
            self.map.setCenter(e.target.getPosition(), true)
        });

        self.map.setFitView(null);


        marker.on('moving', function (e) {
            Opts.onMarkerMoving && Opts.onMarkerMoving(e)
        });

        /**
         * 开始动画
         */
        const startAnimation = () => {
            marker.moveAlong(Opts.lineArr, mergeConfig({
                // 每一段的时长
                // duration: 100,//可根据实际采集时间间隔设置
                // JSAPI2.0 是否延道路自动设置角度在 moveAlong 里设置
                autoRotation: true,
            }, Opts.moveAlongOpts));
        };

        /**
         * 暂停动画
         */
        const pauseAnimation = () => {
            marker.pauseMove();
        }

        /**
         * 继续动画
         */
        const resumeAnimation = () => {
            marker.resumeMove();
        }

        /**
         * 停止动画
         */
        const stopAnimation = () => {
            marker.stopMove();
        }

        return {
            startAnimation,
            pauseAnimation,
            resumeAnimation,
            stopAnimation
        }

    }

}


export default AmapUtils